package com.kun.video.ui.pane;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.kun.video.ui.KunComponent;
import com.kun.video.util.ComUtil;
import com.kun.video.util.CommandExecutor;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Parent;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 增强型图像视图容器，支持框选操作、动态缩放和坐标转换
 * <p>
 * 功能特性：
 * 1. 支持多历史框选记录管理
 * 2. 精确的屏幕坐标与原始图像坐标转换
 * 3. 响应式尺寸调整与性能优化
 * 4. 可扩展的事件处理机制
 *
 * @author gzc
 * @since 2025/4/1
 */
public class KunImageViewBox extends Pane implements KunComponent {
    private static final Logger log = LoggerFactory.getLogger(KunImageViewBox.class);
    private static final String CSS_CLASS = "kun-image-view";
    /**
     * 小数点计算精度位数
     */
    private static final int SCALE = 20;
    // 可配置属性
    /**
     * 框选边框颜色属性
     */
    private final ObjectProperty<Color> strokeColor = new SimpleObjectProperty<>(Color.GREEN);
    /**
     * 框选边框宽度属性
     */
    private final ObjectProperty<Double> strokeWidth = new SimpleObjectProperty<>(1.0);
    /**
     * 是否启用框选功能
     */
    private final BooleanProperty enableSelection = new SimpleBooleanProperty(false);

    // 核心组件
    /**
     * 框选矩形框图形
     */
    private final Rectangle selectionRect = new Rectangle();
    private final ImageView imageView;
    /**
     * 历史框选矩形
     */
    private final ObservableList<Rectangle> historySelectionList = FXCollections.observableArrayList();

    // 状态管理
    private double sourceWidth, sourceHeight = 0D;
    private double zoomRatio = 0;
    private double startX, startY = 0;
    private double offsetX, offsetY = 0;
    private boolean isActive = false;
    private Consumer<Double[]> selectorConsumer;

    // 构造器

    /**
     * 无参构造（禁用框选）
     */
    public KunImageViewBox() {
        this(new ImageView(), false);
    }

    /**
     * 带框选功能控制的构造函数
     *
     * @param enableSelection 是否启用框选功能
     */
    public KunImageViewBox(boolean enableSelection) {
        this(new ImageView(), enableSelection);
    }

    public KunImageViewBox(ImageView imageView, boolean enableSelection) {
        this.imageView = imageView;
        this.enableSelection.set(enableSelection);
        initialize();
    }

    // KunComponent接口实现
    @Override
    public Parent getNode() {
        return this;
    }

    @Override
    public String getStyleName() {
        return CSS_CLASS;
    }

    @Override
    public void initStyleClass() {
        KunComponent.super.initStyleClass();
    }

    @Override
    public void initLayout() {
        getChildren().setAll(this.imageView, this.selectionRect);
    }

    @Override
    public void initStyle() {
        setPickOnBounds(false);
        // 初始化矩形样式
        selectionRect.setFill(Color.TRANSPARENT);
        selectionRect.strokeProperty().bind(strokeColor);
        selectionRect.strokeWidthProperty().bind(strokeWidth);
        selectionRect.setVisible(false);
    }

    @Override
    public void initDimensions() {
    }

    @Override
    public void reactiveDimensions(double width, double height) {
        if (width < 1 || height < 1) {
            return;
        }
        // 计算旧缩放比例
        double oldRatio = this.zoomRatio;
        // 计算新缩放比例
        this.zoomRatio = calculateZoomRatio(sourceWidth, sourceHeight, width, height);
        // 重新计算imageView的宽高
        this.imageView.setFitWidth(BigDecimal.valueOf(sourceWidth).multiply(new BigDecimal(this.zoomRatio)).doubleValue());
        this.imageView.setFitHeight(BigDecimal.valueOf(sourceHeight).multiply(new BigDecimal(this.zoomRatio)).doubleValue());
        log.info("源文件宽度 ->{}, 源文件高度 ->{}，窗口宽度变化 ->{}，高度变化->{}，计算缩放比 -> {}，图片视图宽度 ->{}，图片视图高度 ->{}",
                sourceWidth, sourceHeight, width, height, zoomRatio, imageView.getFitWidth(), imageView.getFitHeight());
        // 计算缩放比例变化量
        double ratioDelta = this.zoomRatio / oldRatio;
        // 调整历史框选位置和尺寸
        historySelectionList.forEach(rect -> {
            rect.setX(rect.getX() * ratioDelta);
            rect.setY(rect.getY() * ratioDelta);
            rect.setWidth(rect.getWidth() * ratioDelta);
            rect.setHeight(rect.getHeight() * ratioDelta);
        });

    }

    @Override
    public void initListeners() {
        // 绑定事件处理
        imageView.addEventHandler(MouseEvent.MOUSE_PRESSED, this::handlePressSelection);
        imageView.addEventHandler(MouseEvent.MOUSE_DRAGGED, this::handleDragSelection);
        imageView.addEventHandler(MouseEvent.MOUSE_RELEASED, this::handleReleaseSelection);
    }

    @Override
    public void dispose() {
    }

    @Override
    public void initData() {
    }

    // 公共API

    public double[] getSelection() {
        // 获取视图偏移位置
        Point2D imageViewPoint = imageView.sceneToLocal(selectionRect.getX() + offsetX, selectionRect.getY() + offsetY, true);
        BigDecimal zoomRatioBigDecimal = new BigDecimal(this.zoomRatio);
        double screenX = BigDecimal.valueOf(imageViewPoint.getX()).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue();
        double screenY = BigDecimal.valueOf(imageViewPoint.getY()).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue();
        double screenW = BigDecimal.valueOf(selectionRect.getWidth()).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue();
        double screenH = BigDecimal.valueOf(selectionRect.getHeight()).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue();
        return new double[]{screenX, screenY, screenW, screenH};
    }

    /**
     * 获取所有历史框选区域的原始坐标
     *
     * @return 包含[x, y, width, height]的列表
     */
    public List<double[]> getAllSelections() {
        return historySelectionList.stream().map(rect -> {
            Point2D imageViewPoint = imageView.sceneToLocal(rect.getX() + offsetX, rect.getY() + offsetY, true);
            return convertToSourceCoordinates(
                    imageViewPoint.getX(),
                    imageViewPoint.getY(),
                    rect.getWidth(),
                    rect.getHeight()
            );
        }).collect(Collectors.toList());
    }

    /**
     * 清除所有历史框选记录
     */
    public void clearSelections() {
        getChildren().removeAll(historySelectionList);
        historySelectionList.clear();
    }

    /**
     * 加载视频指定时间的帧图片
     *
     * @param file 视频文件对象
     * @param file 指定视频第几秒
     */
    public void loadVideoFrameImage(File file, int second) {
        try {
            if (file == null || file.isDirectory()) {
                log.warn("加载视频指定时间的帧图片的图片文件为空");
                return;
            }
            File tempFile = File.createTempFile("temp_frame_", ".jpg");
            String command = StrUtil.format("ffmpeg -ss {} -i {} -vframes 1 -q:v 2 -vsync 0 -y {}",
                    DateUtil.secondToTime(second), file.getAbsolutePath(), tempFile.getAbsolutePath());
            CommandExecutor.executeCommand(command);
            byte[] imageData = FileUtil.readBytes(tempFile);
            Image image = new Image(new ByteArrayInputStream(imageData));
            updateSourceSize(image.getWidth(), image.getHeight());
            ComUtil.uiThreadRun(() -> imageView.setImage(image));
        } catch (Exception e) {
            log.error("视频首帧提取失败: ", e);
        }
    }

    /**
     * 更新原始图像尺寸并重新计算缩放
     *
     * @param sourceWidth  原始宽度
     * @param sourceHeight 原始高度
     */
    public void updateSourceSize(double sourceWidth, double sourceHeight) {
        this.sourceWidth = sourceWidth;
        this.sourceHeight = sourceHeight;
        this.zoomRatio = calculateZoomRatio(sourceWidth, sourceHeight, getPrefWidth(), getPrefHeight());
        this.imageView.setFitWidth(BigDecimal.valueOf(sourceWidth).multiply(new BigDecimal(this.zoomRatio)).doubleValue());
        this.imageView.setFitHeight(BigDecimal.valueOf(sourceHeight).multiply(new BigDecimal(this.zoomRatio)).doubleValue());
    }

    /**
     * 坐标转换：屏幕坐标 -> 原始图像坐标
     *
     * @param viewX 视图X坐标
     * @param viewY 视图Y坐标
     * @param viewW 视图宽度
     * @param viewH 视图高度
     * @return 原始图像坐标数组[x, y, width, height]
     */
    private double[] convertToSourceCoordinates(double viewX, double viewY, double viewW, double viewH) {
        BigDecimal zoomRatioBigDecimal = new BigDecimal(this.zoomRatio);
        return new double[]{
                BigDecimal.valueOf(viewX).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue(),
                BigDecimal.valueOf(viewY).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue(),
                BigDecimal.valueOf(viewW).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue(),
                BigDecimal.valueOf(viewH).divide(zoomRatioBigDecimal, SCALE, RoundingMode.HALF_DOWN).doubleValue()
        };
    }

    // 事件处理方法
    /**
     * 处理鼠标框选点击事件
     *
     * @param event 事件对象
     */
    private void handlePressSelection(MouseEvent event) {
        if (!enableSelection.get()) {
            return;
        }
        if (!event.isPrimaryButtonDown()) {
            return;
        }
        startX = event.getX();
        startY = event.getY();
        selectionRect.setX(startX);
        selectionRect.setY(startY);
        selectionRect.setWidth(0);
        selectionRect.setHeight(0);
        selectionRect.setVisible(true);
        isActive = true;
    }

    /**
     * 处理鼠标框选拖动事件
     *
     * @param event 事件对象
     */
    private void handleDragSelection(MouseEvent event) {
        if (!enableSelection.get()) {
            return;
        }
        if (!isActive) {
            return;
        }
        updateRectDimensions(event.getX(), event.getY());
    }

    /**
     * 处理鼠标框选释放事件
     *
     * @param event 事件对象
     */
    private void handleReleaseSelection(MouseEvent event) {
        if (!enableSelection.get()) {
            return;
        }
        if (!isActive) {
            return;
        }
        isActive = false;
        // 克隆当前矩形并加入历史记录
        Rectangle savedRect = cloneRectangle(selectionRect);
        historySelectionList.add(savedRect);
        // 添加到显示层级
        getChildren().add(savedRect);
        selectionRect.setVisible(false);
        double[] currentSelection = getSelection();
        log.info("框选完成: {}", currentSelection);
        // 回调框选函数
        if (this.selectorConsumer != null) {
            this.selectorConsumer.accept(new Double[]{currentSelection[0], currentSelection[1], currentSelection[2], currentSelection[3]});
        }
    }

    /**
     * 克隆矩形属性
     *
     * @param source 矩形对象
     * @return 克隆对象
     */
    private Rectangle cloneRectangle(Rectangle source) {
        Rectangle rect = new Rectangle();
        rect.setX(source.getX());
        rect.setY(source.getY());
        rect.setWidth(source.getWidth());
        rect.setHeight(source.getHeight());
        rect.setStroke(strokeColor.get());
        rect.setStrokeWidth(strokeWidth.get());
        rect.setFill(Color.TRANSPARENT);
        return rect;
    }


    /**
     * 更新矩形尺寸
     *
     * @param currentX 当前X坐标
     * @param currentY 当前Y坐标
     */
    private void updateRectDimensions(double currentX, double currentY) {
        double minX = Math.min(startX, currentX);
        double minY = Math.min(startY, currentY);
        double width = Math.abs(currentX - startX);
        double height = Math.abs(currentY - startY);

        selectionRect.setX(minX);
        selectionRect.setY(minY);
        selectionRect.setWidth(width);
        selectionRect.setHeight(height);
    }

    /**
     * 计算缩放比例
     *
     * @param sourceWidth  视频原始宽度
     * @param sourceHeight 视频原始高度
     * @param windowWidth  窗口宽度
     * @param windowHeight 窗口高度
     * @return 视频等比例缩放后放到窗口中的缩放比例
     */
    private double calculateZoomRatio(double sourceWidth, double sourceHeight, double windowWidth, double windowHeight) {
        if (sourceWidth < 1 || sourceHeight < 1) {
            return 1;
        }
        // 视频宽高比例
        double videoResolutionRatio = new BigDecimal(sourceWidth).divide(BigDecimal.valueOf(sourceHeight), SCALE, RoundingMode.HALF_DOWN).doubleValue();
        // 窗口宽高比例
        double windowResolutionRatio = new BigDecimal(windowWidth).divide(BigDecimal.valueOf(windowHeight), SCALE, RoundingMode.HALF_DOWN).doubleValue();
        double targetWidthRatio = 1;
        double targetHeightRatio = 1;
        // 如果 videoResolutionRatio > videoResolutionRatio，高度为约束方向，按目标高度缩放。
        if (videoResolutionRatio > windowResolutionRatio) {
            targetWidthRatio = new BigDecimal(windowWidth).divide(BigDecimal.valueOf(sourceWidth), SCALE, RoundingMode.HALF_DOWN).doubleValue();
        }
        // 如果 videoResolutionRatio < videoResolutionRatio，宽度为约束方向，按目标宽度缩放。
        if (videoResolutionRatio < windowResolutionRatio) {
            targetHeightRatio = new BigDecimal(windowHeight).divide(BigDecimal.valueOf(sourceHeight), SCALE, RoundingMode.HALF_DOWN).doubleValue();
        }
        double min = Math.min(targetWidthRatio, targetHeightRatio);
        log.info("视频宽度 -> {}，高度 ->{}，宽高比 -> {}。 窗口宽度 -> {}，高度 -> {}，宽高比 ->{}。缩放比例 -> {}",
                sourceWidth, sourceHeight, videoResolutionRatio, windowWidth, windowHeight, windowResolutionRatio, min);
        return min;
    }

    // 属性访问器
    public ImageView getImageView() {
        return this.imageView;
    }

    public void setImage(WritableImage writableImage) {
        this.imageView.setImage(writableImage);
    }

    public void setImage(Image image) {
        this.imageView.setImage(image);
    }

    public void setSelectorConsumer(Consumer<Double[]> selectorConsumer) {
        this.selectorConsumer = selectorConsumer;
    }

    public void setOffset(double offsetX, double offsetY) {
        this.offsetX = offsetX;
        this.offsetY = offsetY;
    }

}
